﻿using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using Chat.Common;

public enum ClientState
{
    Unstarted,Paused,WaitRcving
}

public class ChatClient
{
    public event Action OnConnectionClosed;
    public string ServerIp { get; set; }
    public int ServerPort { get; set; }    

    TcpClient client;
    NetworkStream stream;
    Task waitTask;
    ManualResetEvent mre;
    
    public ChatClient()
    {
        CurrentState = ClientState.Unstarted;
    }
    /// <summary>
    /// 启动我们的聊天客户端服务
    /// </summary>
    public void Start()
    {
        if(CurrentState == ClientState.Unstarted)
        {
            Debug.Log("Start connecting ……");
            client = new TcpClient();
            try
            {
                client.Connect(ServerIp, ServerPort);
            }
            catch (Exception)
            {
                Debug.Log("连接服务器失败!");
                client.Dispose();
                return;
            }         
            stream = client.GetStream();
            mre = new ManualResetEvent(true);
            waitTask = new Task(WaitForRcvAsync);
            waitTask.Start();
            Debug.Log("connected ……");
        }
    }
    /// <summary>
    /// 暂时停止聊天客户端等待数据
    /// </summary>
    public void Paused()
    {
        if (CurrentState == ClientState.WaitRcving)
        {
            CurrentState = ClientState.Paused;
            mre.Reset();
        }
    }
    /// <summary>
    /// 恢复我们的聊天客户端等待数据
    /// </summary>
    public void Resume()
    {
        if (this.CurrentState == ClientState.Paused)
        {
            CurrentState = ClientState.WaitRcving;
            mre.Set();
        }
    }
    /// <summary>
    /// 关闭我们的聊天客户端服务
    /// </summary>
    public void Destory()
    {
        if(CurrentState != ClientState.Unstarted)
        {
            client.Close();
            stream.Close();
            client.Dispose();
            stream.Dispose();
            
            CurrentState = ClientState.Unstarted;
        }
    }
    /// <summary>
    /// 获取客户端状态
    /// </summary>
    public ClientState CurrentState { get; private set; }
    /// <summary>
    /// 向聊天室服务端发送数据
    /// </summary>
    /// <param name="data">欲被发送的数据</param>
    public void Send(byte[] data)
    {
        if (stream!=null && stream.CanWrite && CurrentState == ClientState.WaitRcving)
            stream.Write(data, 0, data.Length);
    }
    /// <summary>
    /// 向聊天室服务端发送字符串命令
    /// </summary>
    /// <param name="cmd">命令字符串</param>
    public void Send(string cmd)
    {
        Send(Encoding.UTF8.GetBytes(cmd));
    }
    
    /// <summary>
    /// 等待接收远程数据
    /// </summary>
    private void WaitForRcvAsync()
    {
        byte[] data = new byte[Common.maxBufferSize];
        int i=0;
        bool isClosed = false;         
        while (true)
        {
            CurrentState = ClientState.WaitRcving;
            do
            {
                if (client==null || !client.Connected || stream == null || !stream.CanRead)
                    break;
                i = stream.Read(data, 0, data.Length);
                if (i == 0)
                {
                    isClosed = true;
                    break;
                }
                byte[] msg = new byte[i];
                Array.Copy(data, msg, i);
                Task asyncTask = new Task(() => { HandleData(msg); });
                asyncTask.Start();
                mre.WaitOne();

            } while (stream.DataAvailable);
            if (isClosed)
                break;
        }
        // 客户端被中断
        {
            Destory();
            Task task = new Task(OnClientClosed);
            task.Start();
        }    
    }
    /// <summary>
    /// 由于远程服务器原因导致连接被关闭
    /// </summary>
    private void OnClientClosed()
    {
        Debug.Log("Client connection was closed……");
        OnConnectionClosed();
    }
    /// <summary>
    /// 处理收到的远程数据
    /// </summary>
    /// <param name="data"></param>
    private void HandleData(byte[] data)
    {

        // 数据格式:命令头|命令类型|命令|命令尾
        int cmdHeadSize = Common.cmdHeadSize;
        int cmdTailSize = Common.cmdTailSize;
        int cmdTypeSize = Common.cmdTypeSize;
        string rcvDataHead = Common.rcvDataHead;
        string rcvDataTail = Common.rcvDataTail;
        int dataLength = data.Length; // 收到的数据长度
        int minDataSize = cmdHeadSize + cmdTailSize + cmdTypeSize; //有效数据最小长度
        if (dataLength >= minDataSize)
        {
            int cmdSize = dataLength - minDataSize; // 有效命令长度
            byte[] commandHead = new byte[cmdHeadSize];
            byte[] commandTail = new byte[cmdTailSize];
            byte[] commandType = new byte[cmdTypeSize];
            byte[] command = new byte[cmdSize];
            Array.Copy(data, 0, commandHead, 0, cmdHeadSize); // 取出命令头
            Array.Copy(data, cmdHeadSize, commandType, 0, cmdTypeSize); // 取出命令类型
            Array.Copy(data, cmdHeadSize + cmdTypeSize, command, 0, cmdSize); // 取出命令
            Array.Copy(data, dataLength - cmdTailSize, commandTail, 0, cmdTailSize); //命令尾
            // 命令类型转换
            string strCmdHead = Encoding.UTF8.GetString(commandHead);
            string strCmdTail = Encoding.UTF8.GetString(commandTail);
            string strCmdType = Encoding.UTF8.GetString(commandType);
            string strCmd = Encoding.UTF8.GetString(command).Trim();
            // 判断数据是否完整
            if (string.Compare(strCmdHead.Trim(), rcvDataHead, true) == 0 && string.Compare(strCmdTail.Trim(), rcvDataTail, true) == 0)
            {
                Debug.Log($"{strCmdHead}|{strCmdType}|{strCmd}|{strCmdTail}");
                Task asyncTask = new Task(() => { RcvMsgHandleFactory.Create(strCmdType.Trim()).Handle(strCmd); });
                asyncTask.Start();
            }
            else
                Debug.Log("Received data but no intact……"); // 不完整数据
        }
        else
            Debug.Log("Received data but no available……"); // 无效数据
    }

}
